#include "widget.h"
#include "GL/gl.h"
#include <QImage>
#include <math.h>
#include <QTime>

#define BUFFER_OFFSET(offset) ((void*) (offset))

QVector3D cameraPos   = QVector3D(0.0f, 1.0f,  -5.0f); //摄像机初始化在z轴负方向
QVector3D cameraFront = QVector3D(6.12303e-17f, 0.0f, 1.0f);
QVector3D cameraUp    = QVector3D(0.0f, 1.0f,  0.0f);

int deltaTime = 0; // 当前帧与上一帧的时间差
QTime lastTime;

float lastX = 0, lastY = 0;
float pitch = 0, yaw = 90, roll = 0; //初始化摄像机方向 俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll)
float fov = 45.0;

Widget::Widget(QOpenGLWidget *parent)
    :QOpenGLWidget(parent)
{
    setWindowTitle("颜色");
    m_angle = 0.0;
    m_timer = new QTimer;
    m_timer->setInterval(30);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(timeout()));
    m_timer->start();

    lastTime.start();
    setMouseTracking(true);
    firstMouse = 0;
    setWindowState(Qt::WindowMaximized);

    lastX = width()/2;
    lastY = height()/2;

    m_cursor = cursor();
}

Widget::~Widget()
{

}

void Widget::initializeGL()
{
    initializeOpenGLFunctions();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);

    m_program = new QOpenGLShaderProgram(this);

    //编译代码-添加顶点着色器
    m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/triangles.vert");
    //编译代码-添加片段着色器
    m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/triangles.frag");

    //链接着色器
    m_program->link();

    //VAO，VBO数据部分
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
        0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };

    //得到顶点属性名在着色器参数列表中的位置-0
    m_posAttr = m_program->attributeLocation("aPos");
    //    m_colorAttr = m_program->attributeLocation("aColor");

    glGenVertexArrays(1, m_VAO);
    glBindVertexArray(m_VAO[0]);

    glGenBuffers(1, m_VBO);
    glBindBuffer(GL_ARRAY_BUFFER, m_VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    //链接顶点属性
    glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), BUFFER_OFFSET(0));
    //    glVertexAttribPointer(m_colorAttr, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), BUFFER_OFFSET(3*sizeof(GLfloat)));

    glEnableVertexAttribArray(m_posAttr);
    //    glEnableVertexAttribArray(m_colorAttr);

    m_projection = m_program->uniformLocation("projection");
    m_view = m_program->uniformLocation("view");
    m_model = m_program->uniformLocation("model");

    m_objectColor = m_program->uniformLocation("objectColor");
    m_lightColor = m_program->uniformLocation("lightColor");

    //索引缓冲对象
    //    glGenBuffers(1, m_EBO);
    //    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO[0]);
    //    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    //解绑缓存着色器（绑定和解绑的顺序很重要，勿更改）
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // light
    m_program_light = new QOpenGLShaderProgram(this);

    //编译代码-添加顶点着色器
    m_program_light->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/triangles.vert");
    //编译代码-添加片段着色器
    m_program_light->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/light.frag");

    //链接着色器
    m_program_light->link();

    glGenVertexArrays(1, m_VAO_Light);
    glBindVertexArray(m_VAO_Light[0]);

    glBindBuffer(GL_ARRAY_BUFFER, m_VBO[0]);
    glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), BUFFER_OFFSET(0));
    glEnableVertexAttribArray(m_posAttr);

}

void Widget::resizeGL(int w, int h)
{

    glViewport(0, 0, w, h);
}

void Widget::paintGL()
{
    deltaTime = lastTime.elapsed();
    lastTime.restart();

    glClearColor(0.2f, 0.3f, 0.3f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_program->bind();

    m_program->setUniformValue(m_objectColor, QVector3D(1.0f, 0.5f, 0.31f));
    m_program->setUniformValue(m_lightColor, QVector3D(1.0f, 1.0f, 1.0f));

    glBindVertexArray(m_VAO[0]);

    QMatrix4x4 projection, view;

    float ratio = (float)width() / (float)height();

    projection.perspective(fov, ratio, 0.1f, 100.0f);

    view.lookAt(cameraPos, cameraPos+cameraFront, cameraUp);

    m_program->setUniformValue(m_projection, projection);
    m_program->setUniformValue(m_view, view);

    QMatrix4x4 model;
    //        model.scale(QVector3D(0.5, 0.5, 0.5));
    model.translate(QVector3D( 0.0f,  0.0f,  0.0f));
    model.rotate(0.0f, QVector3D(0.5f, 1.0f, 0.0f));

    m_program->setUniformValue(m_model, model);

    glDrawArrays(GL_TRIANGLES, 0, 36);

    m_program->release();

    //light
    m_program_light->bind();

    m_program_light->setUniformValue(m_projection, projection);
    m_program_light->setUniformValue(m_view, view);

    QMatrix4x4 lightPos;
    lightPos.translate(QVector3D( -1.2f, 1.0f, -2.0f));
    lightPos.scale(QVector3D(0.2, 0.2, 0.2));

    m_program_light->setUniformValue(m_model, lightPos);

    glDrawArrays(GL_TRIANGLES, 0, 36);

    m_program_light->release();

}

void Widget::keyPressEvent(QKeyEvent *event)
{
    float cameraSpeed = 0.05f * deltaTime;
    if(event->key() == Qt::Key_A)
    {
        cameraPos -= QVector3D::crossProduct(cameraFront, cameraUp).normalized() * cameraSpeed;
    }
    else if(event->key() == Qt::Key_D)
    {
        cameraPos += QVector3D::crossProduct(cameraFront, cameraUp).normalized() * cameraSpeed;
    }
    else if(event->key() == Qt::Key_W)
    {
        cameraPos += cameraSpeed * cameraFront;
    }
    else if(event->key() == Qt::Key_S)
    {
        cameraPos -= cameraSpeed * cameraFront;
    }
    QOpenGLWidget::keyPressEvent(event);
}

void Widget::mousePressEvent(QMouseEvent *event)
{
    QOpenGLWidget::mousePressEvent(event);
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    //    qInfo()<<event->pos();
    int xpos = event->pos().x();
    int ypos = event->pos().y();

    if(firstMouse<2) // 这个bool变量初始时是设定为true的
    {
        firstMouse++;

        lastX = xpos;
        lastY = ypos;

        //        qInfo()<<firstMouse<<lastX<< lastY<<x()<<y()<<"_"<<event->pos();

        m_cursor.setPos(width()/2, height()/2); //初始化鼠标为中心位置
        if(firstMouse==1) return;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // 注意这里是相反的，因为y坐标是从底部往顶部依次增大的
    lastX = xpos;
    lastY = ypos;

    float sensitivity = 0.1f; // 灵敏度
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw   += xoffset;
    pitch += yoffset;

    if(pitch > 89.0f)
        pitch =  89.0f;
    if(pitch < -89.0f)
        pitch = -89.0f;

    QVector3D front;
    front.setX(cos(pitch/180.0*M_PI) * cos(yaw/180.0*M_PI));
    front.setY(sin(pitch/180.0*M_PI));
    front.setZ(cos(pitch/180.0*M_PI) * sin(yaw/180.0*M_PI));
    cameraFront = front.normalized();

    //    qInfo()<<event->pos() << xoffset << yoffset
    //          << "===" << cameraFront
    //          << pitch << yaw
    //    <<"XXX" << cameraPos+cameraFront;

    QOpenGLWidget::mousePressEvent(event);
}

void Widget::wheelEvent(QWheelEvent *event)
{
    //    qInfo()<<event->angleDelta();
    if(event->angleDelta().y() > 0)
    {
        // scroll up 120
    }
    else if(event->angleDelta().y() < 0)
    {
        // scroll down -120
    }

    if(fov >= 1.0f && fov <= 45.0f)
        fov -= event->angleDelta().y() / 120.0;

    if(fov <= 1.0f)
        fov = 1.0f;
    if(fov >= 45.0f)
        fov = 45.0f;

    QOpenGLWidget::wheelEvent(event);
}

void Widget::timeout()
{
    m_angle+=3.0;
    if(m_angle>360.0) m_angle = 0.0;
    update();
}
